package com.android.example.app3;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.util.Log;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.locks.ReentrantLock;

/**
 * ErrorLogger: 一个完整的日志记录器，用于捕获未捕获的异常，
 * 实现日志轮转，并保留指定数量的备份文件。
 * 兼容 Android 10 (API 29) 及以上版本。
 */
public class ErrorLogger {

    private static final String TAG = "ErrorLogger";
    private static final String LOG_FILE_NAME = "errorlog.txt"; // 主日志文件名
    private static final long MAX_LOG_FILE_SIZE = 1024 * 1024; // 1MB, 日志文件最大大小
    private static final int MAX_BACKUP_LOG_FILES = 5; // 保留的备份文件数量
    private static ErrorLogger instance;
    private final Context context;
    private final File logFile;
    private final File logDir;
    private final ReentrantLock fileLock = new ReentrantLock(); // 用于线程安全

    private ErrorLogger(Context context) {
        this.context = context;
        // 使用应用的私有外部存储目录，兼容 Android 10+
        this.logDir = new File(context.getExternalFilesDir(null), "logs");
        if (!logDir.exists() && !logDir.mkdirs()) {
            Log.e(TAG, "Failed to create log directory: " + logDir.getAbsolutePath());
        }
        this.logFile = new File(logDir, LOG_FILE_NAME);
    }

    /**
     * 获取 ErrorLogger 的单例实例。
     *
     * @param context 应用上下文，最好是 Application Context。
     * @return ErrorLogger 实例。
     */
    public static synchronized ErrorLogger getInstance(Context context) {
        if (instance == null) {
            instance = new ErrorLogger(context.getApplicationContext());
        }
        return instance;
    }

    /**
     * 初始化日志系统，设置全局的 UncaughtExceptionHandler。
     */
    public void initialize() {
        Thread.UncaughtExceptionHandler defaultHandler = Thread.getDefaultUncaughtExceptionHandler();
        Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
            // 记录崩溃日志
            logError("UNCAUGHT EXCEPTION in thread: " + thread.getName(), throwable);

            // 如果有默认的处理器，调用它（通常用于打印原始堆栈和退出应用）
            if (defaultHandler != null) {
                defaultHandler.uncaughtException(thread, throwable);
            }
        });
    }

    /**
     * 记录错误日志（包括异常堆栈）。
     *
     * @param tag       日志标签。
     * @param throwable 异常对象。
     */
    public void logError(String tag, Throwable throwable) {
        fileLock.lock();
        try {
            // 清理旧日志，确保只保留最近的 N 个
            cleanOldLogs(MAX_BACKUP_LOG_FILES);

            // 检查是否需要轮转
            if (logFile.exists() && logFile.length() > MAX_LOG_FILE_SIZE) {
                rotateLogFile();
            }

            // 获取当前时间戳
            String timestamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.getDefault()).format(new Date());

            // 获取应用版本信息
            String versionInfo = "AppVersion: ";
            try {
                PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
                versionInfo += packageInfo.versionName + " (" + packageInfo.versionCode + ")";
            } catch (PackageManager.NameNotFoundException e) {
                versionInfo += "Unknown";
                Log.e(TAG, "Failed to get package info", e);
            }

            // 构建日志内容
            StringBuilder logContent = new StringBuilder();
            logContent.append("\n--- ERROR START ---\n")
                    .append(timestamp).append(" [").append(tag).append("]\n")
                    .append(versionInfo).append("\n");

            if (throwable != null) {
                StringWriter sw = new StringWriter();
                PrintWriter pw = new PrintWriter(sw);
                throwable.printStackTrace(pw);
                logContent.append(sw.toString());
            } else {
                logContent.append("No stack trace available.");
            }

            logContent.append("\n--- ERROR END ---\n");

            // 写入文件
            try (FileWriter writer = new FileWriter(logFile, true)) { // true 表示追加模式
                writer.write(logContent.toString());
            } catch (IOException e) {
                Log.e(TAG, "Failed to write error log", e);
            }
        } finally {
            fileLock.unlock();
        }
    }

    /**
     * 日志轮转：重命名当前日志文件，创建一个新的日志文件。
     */
    private void rotateLogFile() {
        fileLock.lock();
        try {
            if (!logFile.exists()) {
                return;
            }

            // 生成带有时间戳的备份文件名
            String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
            File backupFile = new File(logDir, LOG_FILE_NAME + "." + timestamp + ".bak");

            // 重命名当前日志文件
            if (logFile.renameTo(backupFile)) {
                Log.d(TAG, "Log file rotated. Old log saved as: " + backupFile.getAbsolutePath());
                // 创建一个新的空日志文件
                if (!logFile.createNewFile()) {
                    Log.e(TAG, "Failed to create new log file after rotation.");
                }
            } else {
                Log.e(TAG, "Failed to rotate log file.");
            }

            // 清理旧日志
            cleanOldLogs(MAX_BACKUP_LOG_FILES);
        } catch (IOException e) {
            Log.e(TAG, "Error during log rotation", e);
        } finally {
            fileLock.unlock();
        }
    }

    /**
     * 清理旧的日志备份文件，只保留最近 N 个。
     *
     * @param maxBackupFiles 要保留的备份文件数量。
     */
    public void cleanOldLogs(int maxBackupFiles) {
        fileLock.lock();
        try {
            File[] backupFiles = logDir.listFiles((dir, name) -> name.startsWith(LOG_FILE_NAME + ".") && name.endsWith(".bak"));
            if (backupFiles == null || backupFiles.length <= maxBackupFiles) {
                return; // 没有备份文件或数量足够
            }

            // 按修改时间排序（最早的在前面）
            java.util.Arrays.sort(backupFiles, (f1, f2) -> Long.compare(f1.lastModified(), f2.lastModified()));

            // 删除多余的旧文件
            int filesToDelete = backupFiles.length - maxBackupFiles;
            for (int i = 0; i < filesToDelete; i++) {
                if (backupFiles[i].delete()) {
                    Log.d(TAG, "Deleted old log backup: " + backupFiles[i].getName());
                } else {
                    Log.w(TAG, "Failed to delete old log backup: " + backupFiles[i].getName());
                }
            }
        } finally {
            fileLock.unlock();
        }
    }

    /**
     * 清除所有日志文件（主日志文件和备份文件）。
     *
     * @return true 如果所有文件都成功删除，false 如果有文件删除失败。
     */
    public boolean clearAllLogs() {
        fileLock.lock();
        try {
            boolean success = true;

            // 删除主日志文件
            if (logFile.exists() && !logFile.delete()) {
                Log.w(TAG, "Failed to delete main log file.");
                success = false;
            }

            // 删除所有备份文件
            File[] backupFiles = logDir.listFiles((dir, name) -> name.startsWith(LOG_FILE_NAME + "."));
            if (backupFiles != null) {
                for (File backupFile : backupFiles) {
                    if (!backupFile.delete()) {
                        Log.w(TAG, "Failed to delete log backup: " + backupFile.getName());
                        success = false;
                    } else {
                        Log.d(TAG, "Deleted log backup: " + backupFile.getName());
                    }
                }
            }

            // 创建一个新的空日志文件
            try {
                if (!logFile.createNewFile()) {
                    Log.e(TAG, "Failed to create new empty log file after clear.");
                    success = false;
                }
            } catch (IOException e) {
                Log.e(TAG, "Error creating new log file after clear", e);
                success = false;
            }

            return success;
        } finally {
            fileLock.unlock();
        }
    }

    /**
     * 获取当前主日志文件的 File 对象。
     *
     * @return 当前日志文件的 File 对象，如果不存在则返回 null。
     */
    public File getCurrentLogFilePath() {
        return logFile.exists() ? logFile : null;
    }
}
